/******************************************************************************/
/* File: socketBase.cpp                                                       */
/******************************************************************************/
/* This source code file is the source file for the ThreadBase class - this   */
/* class is used to provide basic thread functionality within a C++ class.    */
/******************************************************************************/

/******************************************************************************/
/* Standard C Includes.                                                       */
/******************************************************************************/
#include <stdio.h>

/******************************************************************************/
/* Include the pthreads library.                                              */
/******************************************************************************/
#include <errno.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>

/******************************************************************************/
/* Include class header file.                                                 */
/******************************************************************************/
#include "socketClass.h"

/******************************************************************************/
/* Helper Class SocketAddress                                                 */
/*                                                                            */
/* This class provides encapsulation of the sockaddr_in and sockadd_in6       */
/* structures used by many IP socket commands.  The class allows us to set    */
/* parts of the structures and retrieve their contents.  This class is mainly */
/* used for convenience and to minimise rewriting of code to support both     */
/* IPv4 and IPv6.                                                             */
/*                                                                            */
/* The public member methods are:                                             */
/*    SetIPAddressWildCard(): Sets the address to the wildcard address.  The  */
/*                            parameter signifies IPv6 or IPv4.               */
/*    SetIPAddress():         Sets the address to that specified in the given */
/*                            IPAddress class.                                */
/*    SetPortNumber():        Sets the port number to the specified value, 0  */
/*                            is the wildcard value.                          */
/*    GetIPAddress():         Sets cAddr equal to the IPAddress contained     */
/*                            within the sockaddr structure.                  */
/*    GetPortNumber():        Sets iPortNumber equal to the port number       */
/*                            contained within the sockaddr structure.        */
/*    (sockaddr *):           Cast to (sockaddr *).  Returns a pointer that   */
/*                            can be used in socket calls.                    */
/*    SizeOf():               Returns the size of the structure.              */
/******************************************************************************/
class SocketAddress {
public:
    SocketAddress();
    void SetIPAddressWildCard(bool bIPv6);
    void SetIPAddress(IPAddress &cAddr);
    void SetPortNumber(int iPortNumber);
    void GetIPAddress(IPAddress &cAddr);
    void GetPortNumber(int &iPortNumber);

    operator sockaddr *() { 
        return psaGeneric; 
    }
    
    socklen_t SizeOf();

protected:
    struct sockaddr_in6 saStruct;
    struct sockaddr     *psaGeneric;
    struct sockaddr_in  *psaIP4Addr;
    struct sockaddr_in6 *psaIP6Addr;
};

/******************************************************************************/
/* SocketAddress Constructor.                                                 */
/*                                                                            */
/* Zeroes the contents of the internal sockaddr_in6 structure and then sets   */
/* all three structure pointers to point to the internal structure.  The      */
/* three pointers allow us to reference the structure as being of all types.  */
/* The actual internal structure is of type sockaddr_in6 because it is the    */
/* largest of all three types.                                                */
/******************************************************************************/
SocketAddress::SocketAddress() {
    bzero(&saStruct, sizeof(saStruct));

    psaGeneric = (struct sockaddr *)&saStruct;
    psaIP4Addr = (struct sockaddr_in *)&saStruct;
    psaIP6Addr = &saStruct;
}

/******************************************************************************/
/* void SetIPAddressWildCard(bool bIPv6)                                      */
/*                                                                            */
/* If we want an IPv6 wildcard address, the sin6_addr value of the sockaddr   */
/* structure is set to in6addr_any, otherwise the sin_addr value of the       */
/* structure is set to INADDR_ANY.                                            */
/******************************************************************************/
void SocketAddress::SetIPAddressWildCard(bool bIPv6) {
    if (bIPv6) {
        psaIP6Addr->sin6_addr = in6addr_any;
    } else {
        psaIP4Addr->sin_addr.s_addr = htonl(INADDR_ANY);
    }
}

/******************************************************************************/
/* void SetIPAddress(IPAddress &cAddr)                                        */
/*                                                                            */
/* Sets the Address part of the sockaddr structure.  First we set the family  */
/* type based on the family type of the provided IP Address.  We then make a  */
/* binary copy of the address stored in cAddr to the appropriate field in the */
/* sockaddr structure.                                                        */
/******************************************************************************/
void SocketAddress::SetIPAddress(IPAddress &cAddr) {
    psaGeneric->sa_family = cAddr.GetAddressFamily();
    if (psaGeneric->sa_family == AF_INET6) {
        bcopy(cAddr.Get_in_addr(), &(psaIP6Addr->sin6_addr), cAddr.Get_in_addr_length());
    } else {
        bcopy(cAddr.Get_in_addr(), &(psaIP4Addr->sin_addr), cAddr.Get_in_addr_length());
    }
}

/******************************************************************************/
/* void SetPortNumber(int iPortNumber)                                        */
/*                                                                            */
/* Sets the port number part of the sockaddr structure.  Based on the value   */
/* of the family type, we set either the sin_port or sin6_port fields of the  */
/* sockaddr structure.  If the family type is not set, we set the IP4 value.  */
/******************************************************************************/
void SocketAddress::SetPortNumber(int iPortNumber) {
    if (psaGeneric->sa_family == AF_INET6) {
        psaIP6Addr->sin6_port = htons(iPortNumber);
    } else {
        psaIP4Addr->sin_port = htons(iPortNumber);
    }
}

/******************************************************************************/
/* void GetIPAddress(IPAddress &cAddr)                                        */
/*                                                                            */
/* Returns the Address stored in the sockaddr structure.  Based on the family */
/* type value, we set the IPAddress parameter equal to either the sin6_addr   */
/* or sin_addr fields.  If the family type is not set, we assume it is IPv4.  */
/******************************************************************************/
void SocketAddress::GetIPAddress(IPAddress &cAddr) {
    if (psaGeneric->sa_family == AF_INET6) {
        cAddr = psaIP6Addr->sin6_addr;
    } else {
        cAddr = psaIP4Addr->sin_addr;
    }
}

/******************************************************************************/
/* void GetPortNumber(int &iPortNumber)                                       */
/*                                                                            */
/* Returns the port number stored in the sockaddr structure.  Based on the    */
/* family type value, we set the iPortNumber parameter equal to either the    */
/* sin6_port or sin_port fields.  If the family type is not set, assume IPv4. */
/******************************************************************************/
void SocketAddress::GetPortNumber(int &iPortNumber) {
    if (psaGeneric->sa_family == AF_INET6) {
        iPortNumber = ntohs(psaIP6Addr->sin6_port);
    } else {
        iPortNumber = ntohs(psaIP4Addr->sin_port);
    }
}

/******************************************************************************/
/* socklen_t SizeOf()                                                         */
/*                                                                            */
/* Returns the size of the structure pointed to by the (sockaddr *) cast.     */
/* The value returned is based on the family type value.  If the family type  */
/* is not set, we return the size of the larger sockaddr_in6 structure so     */
/* that both types are covered.                                               */
/******************************************************************************/
socklen_t SocketAddress::SizeOf() {
    return (psaGeneric->sa_family == AF_INET)?(sizeof(sockaddr_in)):(sizeof(sockaddr_in6));
}

/******************************************************************************/
/* End Class : SocketAddress                                                  */
/******************************************************************************/

/******************************************************************************/
/* Class SocketBase                                                           */
/******************************************************************************/
/* SocketBase Constructor.                                                    */
/*                                                                            */
/* The socket() function is called to create the socket of the specified type.*/
/* The parameters indicate whether the socket will use IPv6 or IPv4 and the   */
/* type of socket (the default being SOCK_STREAM or TCP).  The returned       */
/* descriptor is stored in iSockDesc.  If an error occurs, an exception is    */
/* thrown and the class is not instantiated, the exception indicates which    */
/* error actually occured.                                                    */
/******************************************************************************/
SocketBase::SocketBase(bool bSocketIsIPv6 = false, int iSocketType = SOCK_STREAM) 
          : bIPv6Socket(bSocketIsIPv6), iSocketProto(iSocketType) {
    if ((iSockDesc = socket((bIPv6Socket) ? AF_INET6 : AF_INET, iSocketProto, 0)) < 0) {
        switch (errno) {
            case EPFNOSUPPORT:
            case EAFNOSUPPORT:
            case EPROTONOSUPPORT:   
                throw SocketException(SocketException::errNoProtocolSupport);
            case ENFILE:            
                throw SocketException(SocketException::errKernelMemory);
            case EMFILE:            
                throw SocketException(SocketException::errNoDescriptors);
            case EACCES:            
                throw SocketException(SocketException::errPermissionDenied);
            case ENOMEM:            
                throw SocketException(SocketException::errMemory);
            case EINVAL:            
                throw SocketException(SocketException::errInvalidProtocol);
            default:                
                throw SocketException(SocketException::errUnknown);
        }
    }
}

/******************************************************************************/
/* SocketBase Destructor.                                                     */
/*                                                                            */
/* If the class was successfully instantiated then the socket was created and */
/* iSockDesc is a valid descriptor.  The destructor closes the socket.        */
/******************************************************************************/
SocketBase::~SocketBase() {
    close(iSockDesc);
}

/* Get socket file descriptor because select and nonblock want to do more operations 
 barely on the fd itself. */
int SocketBase::GetSockDesc() {
    return iSockDesc;
}

/* Set socket file descriptor. In order to implement udp accept, udp must assign the 
 existed socket fd to udp receiver. */
void SocketBase::SetSockDesc(int iNewFd) {
    iSockDesc = iNewFd;
}

/* In order to solve the reconnect problem, use this function to reset the socket.*/ 
void SocketBase::ResetSock() {
    int iSock;
    
    if (( iSock = socket((bIPv6Socket) ? AF_INET6 : AF_INET, iSocketProto, 0)) < 0) {
        switch (errno) {
            case EPFNOSUPPORT:
            case EAFNOSUPPORT:
            case EPROTONOSUPPORT:   
                throw SocketException(SocketException::errNoProtocolSupport);
            case ENFILE:            
                throw SocketException(SocketException::errKernelMemory);
            case EMFILE:            
                throw SocketException(SocketException::errNoDescriptors);
            case EACCES:            
                throw SocketException(SocketException::errPermissionDenied);
            case ENOMEM:            
                throw SocketException(SocketException::errMemory);
            case EINVAL:            
                throw SocketException(SocketException::errInvalidProtocol);
            default:                
                throw SocketException(SocketException::errUnknown);
        }
    }
    
    NewSocketDetails(iSock, bIPv6Socket);
}

/******************************************************************************/
/* void NewSocketDetails(int iNewSockDesc, bool bIsIPv6)                      */
/*                                                                            */
/* This protected member method is used to reset the internal socket details  */
/* to match those provided.  It closes the existing socket before setting the */
/* new values of iSockDesc and bIPv6Socket.                                   */
/******************************************************************************/
void SocketBase::NewSocketDetails(int iNewSockDesc, bool bIsIPv6) {
    //close(iSockDesc);
    iSockDesc = iNewSockDesc;
    bIPv6Socket = bIsIPv6;
}

/******************************************************************************/
/* void Connect(IPAddress &cServAddr, int iServPortNo)                        */
/*                                                                            */
/* This method is a wrapper for the socket connect() call and connects the    */
/* socket to the specified server (ip address and port number).  A Socket     */
/* Address is created with the provided ip address and port number and this   */
/* is used to call the connect function.  If the connect() call fails, then   */
/* the correct exception is thrown based on the error that occured.           */
/******************************************************************************/
void SocketBase::Connect(IPAddress &cServAddr, int iServPortNo) {
    // The destination socket address.
    SocketAddress cServer;

    cServer.SetIPAddress(cServAddr);
    cServer.SetPortNumber(iServPortNo);

    if (connect(iSockDesc, (sockaddr *) cServer, cServer.SizeOf()) < 0) {
        
        switch (errno) {
            case EBADF:
            case ENOTSOCK:      
                throw SocketException(SocketException::errBadDescriptor);
            case EFAULT:        
                throw SocketException(SocketException::errIllegalPointer);
            case EISCONN:       
                throw SocketException(SocketException::errAlreadyConnected);
            case ECONNREFUSED:  
                throw SocketException(SocketException::errConnectRefused);
            case ETIMEDOUT:     
                throw SocketException(SocketException::errConnectTimeOut);
            case ENETUNREACH:   
                throw SocketException(SocketException::errNetUnreachable);
            case EADDRINUSE:    
                throw SocketException(SocketException::errAddrInUse);
            case EINPROGRESS:   
                throw SocketException(SocketException::errInProgress);
            case EALREADY:      
                throw SocketException(SocketException::errAlreadyConnecting);
            case EAFNOSUPPORT:  
                throw SocketException(SocketException::errIncorrectAddrFamily);
            case EACCES:        
                throw SocketException(SocketException::errBrdCastNotEnabled);
            default:            
                throw SocketException(SocketException::errUnknown);
        }
    }
}

/******************************************************************************/
/* void Bind(IPAddress &cLocalAddr, int iLocalPort = 0)                       */
/*                                                                            */
/* This member method is a simple wrapper to the sockets bind() function call.*/
/* The method is called with a local IP address and port number to bind the   */
/* socket to.  A default port number of zero is a wildcard and lets the OS    */
/* choose the port number.  If the bind() call fails then the correct         */
/* exception is thrown based on the error that occured.                       */
/******************************************************************************/
void SocketBase::Bind(IPAddress &cLocalAddr, int iLocalPort = 0) {
    SocketAddress cLocal;

    cLocal.SetIPAddress(cLocalAddr);
    cLocal.SetPortNumber(iLocalPort);

    if (bind(iSockDesc, (sockaddr *)cLocal, cLocal.SizeOf()) < 0) {
        
        switch (errno) {
            case EBADF:
            case ENOTSOCK:      
                throw SocketException(SocketException::errBadDescriptor);
            case EINVAL:        
                throw SocketException(SocketException::errAlreadyBound);
            case EACCES:        
                throw SocketException(SocketException::errAddressProtected);
            case EADDRINUSE:    
                throw SocketException(SocketException::errAddrInUse);
            default:            
                throw SocketException(SocketException::errUnknown);
        }
    }
}

/******************************************************************************/
/* void Bind(int iLocalPort = 0)                                              */
/*                                                                            */
/* This member method is a simple wrapper to the sockets bind() function call.*/
/* The method is called with a port number to bind the socket to, a wildcard  */
/* ip address is used to bind to all IP addresses on the system.  A default   */
/* port number of zero is a wildcard and lets the OS choose the port number.  */
/* If the bind() call fails then the correct exception is thrown based on the */
/* error that occured.                                                        */
/******************************************************************************/
void SocketBase::Bind(int iLocalPort = 0) {
    SocketAddress cLocal;

    cLocal.SetIPAddressWildCard(bIPv6Socket);
    cLocal.SetPortNumber(iLocalPort);

    if (bind(iSockDesc, (sockaddr *)cLocal, cLocal.SizeOf()) < 0) {
        
        switch (errno) {
            case EBADF:
            case ENOTSOCK:      
                throw SocketException(SocketException::errBadDescriptor);
            case EINVAL:        
                throw SocketException(SocketException::errAlreadyBound);
            case EACCES:        
                throw SocketException(SocketException::errAddressProtected);
            case EADDRINUSE:    
                throw SocketException(SocketException::errAddrInUse);
            default:            
                throw SocketException(SocketException::errUnknown);
        }
    }
}

void SocketBase::CloseSocket() {
    close(iSockDesc);
}

/******************************************************************************/
/* void Listen(int iBackLog)                                                  */
/*                                                                            */
/* This member method is a simple wrapper to the sockets listen() function    */
/* call.  The iBackLog parameter indicates the number of unconnected sockets  */
/* that can be pending in the socket queue.  If the listen() call fails then  */
/* the correct exception is thrown based on the error that occured.           */
/******************************************************************************/
void SocketBase::Listen(int iBackLog = 1) {
    if (listen(iSockDesc, iBackLog) < 0) {
        
        switch (errno) {
            case EBADF:
            case ENOTSOCK:      
                throw SocketException(SocketException::errBadDescriptor);
            case EOPNOTSUPP:    
                throw SocketException(SocketException::errCantListen);
            default:            
                throw SocketException(SocketException::errUnknown);
        }
    }
}

/******************************************************************************/
/* void Accept(SocketBase *pcConnection, IPAddress &cRemoteAddr,              */
/*             int &iRemotePortNo)                                            */
/*                                                                            */
/* This member method is a simple wrapper to the sockets accept() function    */
/* call.  A SocketAddress class is created to accept the details of the new   */
/* connection before accept() is called.  If the accept() call fails then the */
/* correct exception is thrown based on the error that occured.  If the new   */
/* connection was accepted, the Remote IP Address and Port No. are extracted  */
/* from the Socket Address structure and stored in the method parameters for  */
/* return to the caller.  Finally we set the new socket details for the       */
/* SocketBase class pointer pcConnect with the new socket descriptor and      */
/* whether the socket is an IPv6 socket or not.                               */
/******************************************************************************/
void SocketBase::Accept(SocketBase *pcConnection, IPAddress &cRemoteAddr, int &iRemotePortNo) {
    int           iNewSockDesc;
    SocketAddress cRemote;
    socklen_t     slDummy = cRemote.SizeOf();

    if ((iNewSockDesc = accept(iSockDesc, (sockaddr *) cRemote, &slDummy)) < 0) {
        
        switch (errno) {
            case EBADF:
            case ENOTSOCK:      
                throw SocketException(SocketException::errBadDescriptor);
            case EAFNOSUPPORT:  
                throw SocketException(SocketException::errNotStreamSock);
            case EFAULT:        
                throw SocketException(SocketException::errIllegalPointer);
            case EAGAIN:        
                throw SocketException(SocketException::errNoPendingConnections);
            case EPERM:         
                throw SocketException(SocketException::errFirewall);
            case ENOMEM:        
                throw SocketException(SocketException::errMemory);
            default:            
                throw SocketException(SocketException::errUnknown);
        }
    }
    
    cRemote.GetPortNumber(iRemotePortNo);
    cRemote.GetIPAddress(cRemoteAddr);

    // The new socket details for the SocketBase class pointer pcConnect with the new socket descriptor.
    pcConnection->NewSocketDetails(iNewSockDesc, (cRemoteAddr.GetAddressFamily() == AF_INET6));
}

/******************************************************************************/
/* int Recv(char *pBuffer, int iBufLength, unsigned int uiFlags)              */
/*                                                                            */
/* This member method is a simple wrapper to the sockets recvfrom() function  */
/* call.  The recvfrom() function is called with the proper parameters, NULL  */
/* is placed for obtaining the source address information.  The number of     */
/* bytes read is returned.  If an error occurs then the correct exception is  */
/* thrown based on the error that occured.  If the method terminates normally */
/* then we return the actual number of bytes read.                            */
/******************************************************************************/
int SocketBase::Recv(char *pBuffer, int iBufLength, unsigned int uiFlags) {
    int iBytesRead;

    if ((iBytesRead = recvfrom(iSockDesc, pBuffer, iBufLength, uiFlags, NULL, NULL)) < 0) {
        
        switch (errno) {
            case EBADF:
            case ENOTSOCK:
                throw SocketException(SocketException::errBadDescriptor);
            case ENOTCONN:  
                throw SocketException(SocketException::errNotConnected);
            case EAGAIN:    
                throw SocketException(SocketException::errWouldBlock);
            case EINTR:     
                throw SocketException(SocketException::errInterrupted);
            case EFAULT:    
                throw SocketException(SocketException::errIllegalPointer);
            case EINVAL:    
                throw SocketException(SocketException::errInvalidArgument);
            default:        
                throw SocketException(SocketException::errUnknown);
        }
    }
    if (iBytesRead) 
        return iBytesRead;
        
    throw SocketException(SocketException::errNotConnected);
}

/******************************************************************************/
/* int RecvFrom(char *pBuffer, int iBufLength, unsigned int uiFlags,          */
/*              IPAddress &SourceIP, int &SourcePortNumber)                   */
/*                                                                            */
/* This member method is a simple wrapper to the sockets recvfrom() function  */
/* call.  The recvfrom() function is called with the proper parameters, with  */
/* a SocketAddress class being used to obtain the details of the source of    */
/* the data.  The number of bytes read is returned.  If an error occurs then  */
/* the correct exception is thrown based on the error that occured.  If the   */
/* method terminates normally, then the source IP Address and port number are */
/* extracted from the SocketAddress class into the parameter variables, and   */
/* we return the actual number of bytes read.                                 */
/******************************************************************************/
int SocketBase::RecvFrom(char *pBuffer, int iBufLength, unsigned int uiFlags,
                         IPAddress &cSourceIP, int &iSourcePortNumber) {
    SocketAddress cSource;
    socklen_t     slDummy = cSource.SizeOf();
    int           iBytesRead;

    if ((iBytesRead = recvfrom(iSockDesc, pBuffer, iBufLength, uiFlags, (sockaddr *)cSource, &slDummy)) < 0) {
        
        switch (errno) {
            case EBADF:
            case ENOTSOCK:  
                throw SocketException(SocketException::errBadDescriptor);
            case ENOTCONN:  
                throw SocketException(SocketException::errNotConnected);
            case EAGAIN:    
                throw SocketException(SocketException::errWouldBlock);
            case EINTR:     
                throw SocketException(SocketException::errInterrupted);
            case EFAULT:    
                throw SocketException(SocketException::errIllegalPointer);
            case EINVAL:    
                throw SocketException(SocketException::errInvalidArgument);
            default:        
                throw SocketException(SocketException::errUnknown);
        }
    }
    
    if (iBytesRead) {
        cSource.GetIPAddress(cSourceIP);
        cSource.GetPortNumber(iSourcePortNumber);
        return iBytesRead;
    }
    
    throw SocketException(SocketException::errNotConnected);
}

/* With a zero flags argument, send() is equivalent to write(2).  */
/******************************************************************************/
/* int Send(char *pPayload, int iPayloadLength, unsigned int uiFlags)         */
/******************************************************************************/
int SocketBase::Send(char *pPayload, int iPayloadLength, unsigned int uiFlags) {
    int iBytesRead;

    if ((iBytesRead = send(iSockDesc, pPayload, iPayloadLength, uiFlags)) < 0) {
        
        switch (errno) {
            case EBADF:
            case ENOTSOCK:  
                throw SocketException(SocketException::errBadDescriptor);
            case EAGAIN:    
                throw SocketException(SocketException::errWouldBlock);
            case EINTR:     
                throw SocketException(SocketException::errInterrupted);
            case EFAULT:    
                throw SocketException(SocketException::errIllegalPointer);
            case EINVAL:    
                throw SocketException(SocketException::errInvalidArgument);
            case EMSGSIZE:  
                throw SocketException(SocketException::errMessageSizeTooBig);
            case ENOBUFS:
            case ENOMEM:    
                throw SocketException(SocketException::errKernelMemory);
            case EPIPE:     
                throw SocketException(SocketException::errNotConnected);
            default:        
                throw SocketException(SocketException::errUnknown);
        }
    }
    return iBytesRead;
}

/******************************************************************************/
/* int SendTo(char *pPayload, int iPayloadLength, unsigned int uiFlags,       */
/*            IPAddress &cDestinationIP, int iDestinationPortNumber)          */
/******************************************************************************/
int SocketBase::SendTo(char *pPayload, int iPayloadLength, unsigned int uiFlags,
                   IPAddress &cDestinationIP, int iDestinationPortNumber) {

    SocketAddress cDestination;
    int           iBytesRead;

    cDestination.SetIPAddress(cDestinationIP);
    cDestination.SetPortNumber(iDestinationPortNumber);

    if ((iBytesRead = sendto(iSockDesc, pPayload, iPayloadLength, uiFlags, (sockaddr *)cDestination, cDestination.SizeOf())) < 0) {
        
        switch (errno) {
            case EBADF:
            case ENOTSOCK:  
                throw SocketException(SocketException::errBadDescriptor);
            case EAGAIN:    
                throw SocketException(SocketException::errWouldBlock);
            case EINTR:     
                throw SocketException(SocketException::errInterrupted);
            case EFAULT:    
                throw SocketException(SocketException::errIllegalPointer);
            case EINVAL:    
                throw SocketException(SocketException::errInvalidArgument);
            case EMSGSIZE:  
                throw SocketException(SocketException::errMessageSizeTooBig);
            case ENOBUFS:
            case ENOMEM:    
                throw SocketException(SocketException::errKernelMemory);
            case EPIPE:     
                throw SocketException(SocketException::errNotConnected);
            default:        
                throw SocketException(SocketException::errUnknown);
        }
    }
    return iBytesRead;
}

/******************************************************************************/
/* void GetSockName(IPAddress &cLocalAddr, int &iLocalPort)                   */
/*                                                                            */
/* This member method is a simple wrapper to the sockets getsockname()        */
/* function call.  The getsockname() function is called with a SocketAddress  */
/* class being used to obtain the details of the local side of the socket     */
/* connection.  The details (IP Address and port number) are then extracted   */
/* into the method parameters.  If an error occurs then the correct exception */
/* is thrown based on the error that occured.                                 */
/******************************************************************************/
void SocketBase::GetSockName(IPAddress &cLocalAddr, int &iLocalPort) {
    SocketAddress cLocal;
    socklen_t     slDummy = cLocal.SizeOf();

    if (getsockname(iSockDesc, (sockaddr *)cLocal, &slDummy) < 0) {
        
        switch (errno) {
            case EBADF:
            case ENOTSOCK:  
                throw SocketException(SocketException::errBadDescriptor);
            case ENOBUFS:   
                throw SocketException(SocketException::errKernelMemory);
            case EFAULT:    
                throw SocketException(SocketException::errIllegalPointer);
            default:        
                throw SocketException(SocketException::errUnknown);
        }
    }
    cLocal.GetIPAddress(cLocalAddr);
    cLocal.GetPortNumber(iLocalPort);
}

/******************************************************************************/
/* void GetPeerName(IPAddress &cRemoteAddr, int &iRemotePort)                 */
/*                                                                            */
/* This member method is a simple wrapper to the sockets getpeername()        */
/* function call.  The getpeername() function is called with a SocketAddress  */
/* class being used to obtain the details of the remote side of the socket    */
/* connection.  The details (IP Address and port number) are then extracted   */
/* into the method parameters.  If an error occurs then the correct exception */
/* is thrown based on the error that occured.                                 */
/******************************************************************************/
void SocketBase::GetPeerName(IPAddress &cRemoteAddr, int &iRemotePort) {
    SocketAddress cRemote;
    socklen_t     slDummy = cRemote.SizeOf();

    if (getpeername(iSockDesc, (sockaddr *)cRemote, &slDummy) < 0) {
        
        switch (errno) {
            case EBADF:
            case ENOTSOCK:  
                throw SocketException(SocketException::errBadDescriptor);
            case ENOBUFS:   
                throw SocketException(SocketException::errKernelMemory);
            case EFAULT:    
                throw SocketException(SocketException::errIllegalPointer);
            default:        
                throw SocketException(SocketException::errUnknown);
        }
    }
    cRemote.GetIPAddress(cRemoteAddr);
    cRemote.GetPortNumber(iRemotePort);
}

/******************************************************************************/
/* void GetSockOpt(int iCodeLevel, int iOptionName, void *pOptionData,        */
/*                 int &iDataLength)                                          */
/*                                                                            */
/* This member method is a simple wrapper to the socket getsockopt() function */
/* call.  The getsockopt() function is called with the provided parameters to */
/* obtain the desired value.  If an error occurs then the correct exception   */
/* is thrown based on that error.                                             */
/******************************************************************************/
void SocketBase::GetSockOpt(int iCodeLevel, int iOptionName, void *pOptionData, int &iDataLength) {
    
    if (getsockopt(iSockDesc, iCodeLevel, iOptionName, pOptionData, (socklen_t *)&iDataLength) < 0) {
        
        switch (errno) {
            case EBADF:
            case ENOTSOCK:      
                throw SocketException(SocketException::errBadDescriptor);
            case ENOPROTOOPT:   
                throw SocketException(SocketException::errOptionNotSupported);
            case EFAULT:        
                throw SocketException(SocketException::errIllegalPointer);
            default:            
                throw SocketException(SocketException::errUnknown);
        }
    }
}

/******************************************************************************/
/* void SetSockOpt(int iCodeLevel, int iOptionName, const void *pOptionData,  */
/*                 int iDataLength)                                           */
/*                                                                            */
/* This member method is a simple wrapper to the socket setsockopt() function */
/* call.  The setsockopt() function is called with the provided parameters to */
/* set the desired option.  If an error occurs then the correct exception is  */
/* thrown based on that error.                                                */
/******************************************************************************/
void SocketBase::SetSockOpt(int iCodeLevel, int iOptionName, const void *pOptionData, int iDataLength) {
    
    if (setsockopt(iSockDesc, iCodeLevel, iOptionName, pOptionData, iDataLength) < 0) {
        
        switch (errno) {
            case EBADF:
            case ENOTSOCK:      
                throw SocketException(SocketException::errBadDescriptor);
            case ENOPROTOOPT:   
                throw SocketException(SocketException::errOptionNotSupported);
            case EFAULT:        
                throw SocketException(SocketException::errIllegalPointer);
            default:            
                throw SocketException(SocketException::errUnknown);
        }
    }
}

/******************************************************************************/
/* End of File: socketBase.cpp                                                */
/******************************************************************************/
